//+------------------------------------------------------------------+ //| ^L_Correlation.mq4 | //| Copyright © 2008, lotos4u | //| lotos4u@gmail.com | //+------------------------------------------------------------------+ #property copyright "Copyright © 2008, lotos4u" #property link "lotos4u@gmail.com" //+------------------------------------------------------------------+ //| defines | //+------------------------------------------------------------------+ #define CLOSE_MODE 0 //цена закрытия бара #define CLOSEOPEN_MODE 1 //разница между ценой закрытия и открытия бара #define CLOSE_RELATIVE_MODE 2 //отношение цены закрытия к максимальной цене бара #define CLOSEOPEN_RELATIVE_MODE 3 //отношение разницы цен закрытия и открытия к максимальной цене бара #define FULL_AVG_MODE 4 //среднее значение бара (сумма на 4) #define FULL_AVG_RELATIVE_MODE 5 //среднее значение бара, деленное на максимальное #define HIGHLOW_MODE 6 //разница между максимальной и минимальной ценой бара #define HIGHLOW_AVG_MODE 7 //среднее значение бара по макс. и мин. ценам #define HIGHLOW_RELATIVE_MODE 8 //отношение разницы между макс. и мин. ценой бара к макс. цене #define HIGHLOW_AVG_RELATIVE_MODE 9 //отношение среднего значения бара (по макс. и мин. ценам) к макс. цене бара #property indicator_separate_window #property indicator_minimum -1 #property indicator_maximum 1 #property indicator_buffers 3 #property indicator_color1 Aqua //Коэффициент корреляции #property indicator_color2 Blue //Скользящее среднее от коэф. корреляции #property indicator_color3 Red //Существенный точки коэффициента корреляции #property indicator_width1 1 //Толщина линии коэ. корреляции #property indicator_width2 2 //Толщина линии скользящей средней #property indicator_width3 1 //Размер существенных точек #property indicator_level1 0.75 #property indicator_level2 0.5 #property indicator_level3 0.0 #property indicator_level4 -0.5 #property indicator_level5 -0.75 #property indicator_levelcolor BlueViolet #include #include extern int Mode = FULL_AVG_MODE; //Параметр определяет, что именно коррелируем extern bool AutoCorrelation = false; //Если ИСТИНА, то параметр Pair игнорируется и вычисляется автокорреляция со сдвигом AutoCorrelationShift extern string Pair = "GBPUSD"; //Символическое имя инструмента, корреляция с которым интересует extern bool ShowCorrelation = false; //Если ИСТИНА, то кривая коэффициента корреляции отображается extern bool ShowMA = false; //Если ИСТИНА, то кривая скользящей средней отображается extern bool ShowHistogram = true; //Если ИСТИНА, то гистограмма значений коэффициента корреляции отображается extern bool ShowSummary = true; //Если ИСТИНА, то итоговая надпись отображается extern int ShowEssentialPoints = -1; //Количество существенных точек для отображения. Если =0, то отображаются все точки, если =-1, то не отображаются extern int ShowEssentialRanges = -1; //Количество границ существенных интервалов (вертикальные линии) для отображения. Если =0, то отображаются границы, если =-1, то не отображаются extern int ShowEssentialRectangles = -1; //Количество существенных областей (прямоугольники на чарте) для отображения. Если =0, то отображаются все области, если =-1, то не отображаются extern int CorrelationRadius = 0; //Количество баров, на которых ищется соответствие двух сигналов extern int MA_Period = 10; //Период скользящей средней extern int ResultingBars = 0; //Количество баров, по которым определяется итоговое среднее значение коэффициента корреляции extern int AutoCorrelationShift = 0; //В режиме автокорреляции параметр определяет количество баров, на которое смещаем оригинальный сингал (в режиме корреляции игнорируется) extern double EssentialLevel = 0.7; //Существенный уровень, т.е. экстремальные значения коэф.корреляции выше него будут считаться существенными точками extern string FontName = "Verdana"; //Имя шрифта, которым выводится итоговая подпись в окне extern int FontSize = 9; //Размер шрифта extern color FontColor = White; //Цвет шрифта extern color CorrelationColor = indicator_color1; //Цвет кривой коэф. корреляции extern color MAColor = indicator_color2; //Цвет кривой скользящей средней extern color HistoColor = Orange; //Цвет гистограммы значений коэф. корреляции extern color EssentialPointsColor = indicator_color3; //Цвет существенных точек extern color EssentialRangesColor = Red; //Цвет границ существенных областей extern color EssentialRectanglesColor = Olive; //Цвет прямоугольников существенных областей double CorrelationBuffer[]; double AverageCorrelationBuffer[]; double EssentialPointsBuffer[]; double Correlation[], AvgCorrelation[], ArrayX[], ArrayY[], ValidX[], ValidY[]; double AverageX, AverageY, AverageXY, DispersionX, DispersionY, CovariationXY, HistogramMax = 0.0, AverageCorrelation; int CalculateCounter, ValidDataCounter, AverageLength, ValidDataLength; string ShortName = "L_Correlation", HistoName = " Histo ", SummaryLabel = " Label "; int IndicatorWindow, HistogramBars = 200, HistogramValues[]; //////////////////////////////////////////////////////////////////////////////////////////// //Инициализация индикатора //////////////////////////////////////////////////////////////////////////////////////////// int init() { //Если символ не указан, то корреляция будет вычислятся с самим символом (НО ЭТО НЕ Автокорреляция!) if(Pair == "") Pair = Symbol(); //Автоматический выбор сдвига автокорреляции if(CorrelationRadius == 0) { switch(Period()) { case PERIOD_M1: CorrelationRadius = 10; break;//10 min case PERIOD_M5: CorrelationRadius = 12; break;//hour case PERIOD_M15: CorrelationRadius = 12; break;//3 hours case PERIOD_M30: CorrelationRadius = 12; break;//6 hours case PERIOD_H1: CorrelationRadius = 12; break;//12 hours case PERIOD_H4: CorrelationRadius = 6; break;//week case PERIOD_D1: CorrelationRadius = 5; break;//month case PERIOD_W1: CorrelationRadius = 12; break;//year case PERIOD_MN1: CorrelationRadius = 6; break;//year } } //Автоматический выбор сдвига автокорреляции if(AutoCorrelation && AutoCorrelationShift == 0) { switch(Period()) { case PERIOD_M1: AutoCorrelationShift = 60; break;//hour case PERIOD_M5: AutoCorrelationShift = 12; break;//hour case PERIOD_M15: AutoCorrelationShift = 96; break;//day case PERIOD_M30: AutoCorrelationShift = 48; break;//day case PERIOD_H1: AutoCorrelationShift = 120; break;//week case PERIOD_H4: AutoCorrelationShift = 30; break;//week case PERIOD_D1: AutoCorrelationShift = 22; break;//month case PERIOD_W1: AutoCorrelationShift = 52; break;//year case PERIOD_MN1: AutoCorrelationShift = 12; break;//year } } //Формируем имя окна в зависимости от типа и параметров индикатора if(AutoCorrelation) ShortName = ShortName + " (" + Symbol() + " Auto, " + Mode + ", " + CorrelationRadius + ", " + AutoCorrelationShift + ", " + MA_Period + ")"; else ShortName = ShortName + " (" + Symbol() + "-" + Pair + ", " + Mode + ", " + CorrelationRadius + ", " + MA_Period + ")"; IndicatorShortName(ShortName); //Буфер с коэффициентом корреляции SetIndexStyle(0, DRAW_LINE, EMPTY, EMPTY, CorrelationColor); SetIndexBuffer(0, CorrelationBuffer); SetIndexEmptyValue(0, 2.0); //Буфер со скользящей средней от коэф. корреляции SetIndexStyle(1, DRAW_LINE, EMPTY, EMPTY, MAColor); SetIndexBuffer(1, AverageCorrelationBuffer); SetIndexEmptyValue(1, 2.0); //Буфер с существенными точками SetIndexStyle(2, DRAW_ARROW, EMPTY, EMPTY, EssentialPointsColor); SetIndexArrow(2, 108); SetIndexBuffer(2, EssentialPointsBuffer); SetIndexEmptyValue(2, 2.0); CalculateCounter = 0; ValidDataCounter = 0; CreateSummaryLabels(); return(0); } //////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// //Удаление индикатора //////////////////////////////////////////////////////////////////////////////////////////// int deinit() { CalculateCounter = 0; ValidDataCounter = 0; DeleteObjectsByPhrase(ShortName); return(0); } //////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////////////////// int start() { CreateSummaryLabels(); //Индикатор перерисовывается только в начале нового бара if(CalculateCounter > 0 && Volume[0] > 1) { return(0); } int error, essCounter, limit; string Name; IndicatorWindow = WindowFind(ShortName); if(AutoCorrelation) ValidDataLength = Bars; else ValidDataLength = MathMin(Bars, iBars(Pair, 0)); CreateValidData(); ArrayResize(Correlation, ValidDataLength);ArrayInitialize(Correlation, 0.0);ArraySetAsSeries(Correlation, true); ArrayResize(AvgCorrelation, ValidDataLength);ArrayInitialize(AvgCorrelation, 0.0);ArraySetAsSeries(AvgCorrelation, true); for(int i = 0; i < ValidDataLength; i++) { CreateDataArrays(i); Correlation[i] = getCorrelation(ArrayX, ArrayY); if(ShowCorrelation) CorrelationBuffer[i] = Correlation[i]; } //Формируем массив скользящей средней от коэф. корреляции for(i = 0; i < ValidDataLength; i++) { AvgCorrelation[i] = iMAOnArray(Correlation, 0, MA_Period, 0, MODE_SMA, i); if(ShowMA)//Если требуется отображение - записываем в буфер AverageCorrelationBuffer[i] = AvgCorrelation[i]; } essCounter = 0;//Счетчик существенных точек for(i = 0; i < ValidDataLength; i++) { if((MathAbs(Correlation[i]) > EssentialLevel) && ((MathAbs(Correlation[i]) > MathAbs(Correlation[i-1])) && (MathAbs(Correlation[i]) > MathAbs(Correlation[i+1])))) { essCounter++;//Еще одна существенная точка //Отметка для самой точки if((ShowEssentialPoints == 0) || (essCounter <= ShowEssentialPoints)) { EssentialPointsBuffer[i] = Correlation[i]; } //Границы существенных областей if(AutoCorrelation && (ShowEssentialRanges == 0) || (essCounter <= ShowEssentialRanges)) { Name = ShortName + " VLine1 " + i; ObjectCreate(Name, OBJ_VLINE, 0, Time[i], Correlation[i]); ObjectSet(Name, OBJPROP_COLOR, EssentialRangesColor); Name = ShortName + " VLine2 " + i; ObjectCreate(Name, OBJ_VLINE, 0, Time[AutoCorrelationShift + i], Correlation[i]); ObjectSet(Name, OBJPROP_COLOR, EssentialRangesColor); } //Прямоугольники существенных областей if(AutoCorrelation && (ShowEssentialRectangles == 0) || (essCounter <= ShowEssentialRectangles)) { Name = ShortName + " Rect1 " + i; ObjectCreate(Name, OBJ_RECTANGLE, 0, Time[CorrelationRadius + i], High[iHighest(Symbol(), 0, MODE_HIGH, CorrelationRadius, i)], Time[i], Low[iLowest(Symbol(), 0, MODE_LOW, CorrelationRadius, i)]); ObjectSet(Name, OBJPROP_COLOR, EssentialRectanglesColor); ObjectSet(Name, OBJPROP_BACK, true); Name = ShortName + " Rect2 " + i; ObjectCreate(Name, OBJ_RECTANGLE, 0, Time[CorrelationRadius + AutoCorrelationShift + i], High[iHighest(Symbol(), 0, MODE_HIGH, CorrelationRadius, AutoCorrelationShift+i)], Time[AutoCorrelationShift+i], Low[iLowest(Symbol(), 0, MODE_LOW, CorrelationRadius, AutoCorrelationShift+i)]); ObjectSet(Name, OBJPROP_COLOR, EssentialRectanglesColor); ObjectSet(Name, OBJPROP_BACK, true); } } } //Уточняем количество баров, по которому считать средний коэф. корреляции if(ResultingBars == 0) AverageLength = ValidDataCounter; else AverageLength = ResultingBars; //Средний коэффициент корреляции AverageCorrelation = iMAOnArray(Correlation, 0, AverageLength, 0, MODE_SMA, 0); //Определяем массив, по которому будет строиться гистограмма значений коэффициента корреляции HistogramMax = 0.0; if(ValidDataLength > 0) CreateHistogramArray(); //Построение гистограммы значений коэффициента корреляции if(ShowHistogram && ValidDataLength > 0) { //Стираем старую гистограмму DeleteObjectsByPhrase(ShortName + HistoName); double StartX, StartY, EndX, EndY; //Строим гистограмму трендовыми линиями for(i = -HistogramBars/2; i < HistogramBars/2; i++) { if(HistogramValues[i + HistogramBars/2] == 0)continue; Name = ShortName + HistoName + i;//Имя объекта столбика гистограммы StartX = Time[0];//Горизонтальная координата начала столбика StartY = i*2.0/HistogramBars;//Вертикальная координата начала столбика EndX = Time[0 + 1*HistogramValues[i + HistogramBars/2]];//Горизонтальная координата конца столбика EndY = StartY;//Вертикальная координата конца столбика ObjectCreate(Name, OBJ_TREND, IndicatorWindow, StartX, StartY, EndX, EndY); ObjectSet(Name, OBJPROP_RAY, false); ObjectSet(Name, OBJPROP_BACK, true); ObjectSet(Name, OBJPROP_COLOR, HistoColor); } } //Вывод итоговых подписей if(ShowSummary) ShowSummary(essCounter); CalculateCounter = MathMin(1, ValidDataLength); return(0); } //////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// //Формирует массив, по которому будет строится гистограмма значений коэффициента корреляции //(Аналог функции распределения) //////////////////////////////////////////////////////////////////////////////////////////// void CreateHistogramArray() { double level, levelNext, Upper = 1.0, Lower = -1.0, step; int size = ArraySize(Correlation), maxValue = 0; ArrayResize(HistogramValues, HistogramBars); ArrayInitialize(HistogramValues, 0); //В цикле определяем "вес" значений КК //Всего рассматриввается HistogramBars значений из диапазона от -1 до +1 //Весом значения называем количество значений КК, которые укладываются в значение step = (Upper - Lower)/HistogramBars;//"толщина" столбика for(int j = 0; j < ArraySize(Correlation); j++) { for(int i = 0; i < HistogramBars; i++) { level = (i-HistogramBars/2)*step;//Нижняя граница столбика levelNext = (i-HistogramBars/2+1)*step;//Верхняя граница столбика if(Correlation[j] >= level && Correlation[j] < levelNext)//Если значение КК попадает по диапазон текущего столбика { HistogramValues[i]++;//наращиваем длину столбика на 1 if(maxValue < HistogramValues[i])//Если надо, то уточняем значение максимума гистограммы { maxValue = HistogramValues[i]; HistogramMax = level; } break;//Переходим сразу к следующему значению КК } } } } //////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// //Вывод итоговой надписи внизу окна //////////////////////////////////////////////////////////////////////////////////////////// void ShowSummary(int essCounter) { string Text1, Text2; if(AutoCorrelation) Text2 = "Автокорреляция"; else //Среднее значение КК по указанному числу баров Text2 = "Среднее: Макс. гистограммы/MA = " + DoubleToStr(HistogramMax, 2) + "/" + DoubleToStr(AverageCorrelation, 2) + " (" + AverageLength + " из " + ValidDataLength + " бар)"; //Количество существенных значений КАК Text1 = "Существенных точек (>" + DoubleToStr(EssentialLevel, 2) + "): " + essCounter; ObjectSetText(ShortName + SummaryLabel + "1", Text1, FontSize, FontName, FontColor); ObjectSetText(ShortName + SummaryLabel + "2", Text2, FontSize, FontName, FontColor); } //////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// //Возвращает значение первой случайной функции //////////////////////////////////////////////////////////////////////////////////////////// double getX(int shift) { return(getSymbol(Symbol(), shift, Mode)); } //////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// //Возвращает значение второй случайной функции //////////////////////////////////////////////////////////////////////////////////////////// double getY(int shift) { if(AutoCorrelation) return(getSymbol(Symbol(), AutoCorrelationShift + shift, Mode)); else return(getSymbol(Pair, shift, Mode)); } //////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// //Возвращает ИСТИНА, если найдены ненулевые котировки первого инструмента //////////////////////////////////////////////////////////////////////////////////////////// bool isValidPriceX(int shift) { return(iOpen(Symbol(), 0, shift) > 0.0); } //////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// //Возвращает ИСТИНА, если найдены ненулевые котировки второго инструмента //////////////////////////////////////////////////////////////////////////////////////////// bool isValidPriceY(int shift) { if(AutoCorrelation) return(iOpen(Symbol(), 0, AutoCorrelationShift + shift) > 0.0); else return(iOpen(Pair, 0, shift) > 0.0); } //////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// //Вырезает из подготовленных массивов данных куски для расчета коэф. корреляции //////////////////////////////////////////////////////////////////////////////////////////// void CreateDataArrays(int StartBar) { ArrayResize(ArrayX, CorrelationRadius); ArrayResize(ArrayY, CorrelationRadius); ArrayInitialize(ArrayX, 0.0); ArrayInitialize(ArrayY, 0.0); datetime timeX, timeY; int DataCounter = 0, yIndex = StartBar, xIndex = StartBar; while(xIndex < StartBar + CorrelationRadius || xIndex < ArraySize(ValidX)) { if(ValidX[xIndex] > 0.0 && ValidY[yIndex] > 0.0)//Если нужные котировки есть { ArrayX[DataCounter] = ValidX[xIndex]; ArrayY[DataCounter] = ValidY[yIndex]; DataCounter++; } yIndex++; xIndex++; } //Если доступные данные непокрывают радиус корреляции - то считаем, что данных нет вообще if(DataCounter < CorrelationRadius) {ArrayResize(ArrayX, 0); ArrayResize(ArrayY, 0);} } //////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// //Формирует подготовленные массивы данных, в которых учтены возможные расхождения во //временах баров двух инструментов //////////////////////////////////////////////////////////////////////////////////////////// void CreateValidData() { ArrayResize(ValidX, ValidDataLength); ArrayResize(ValidY, ValidDataLength); ArrayInitialize(ValidX, 0.0); ArrayInitialize(ValidY, 0.0); datetime timeX, timeY; int yIndex = - 1; ValidDataCounter = 0; for(int xIndex = 0; xIndex < ValidDataLength; xIndex++) { yIndex++; timeX = iTime(Symbol(), 0, xIndex); timeY = iTime(Pair, 0, yIndex); if(timeX == NULL || timeY == NULL)break; if(AutoCorrelation) { if(!(isValidPriceX(xIndex) && isValidPriceY(yIndex))) { Sleep(2000); if(!(isValidPriceX(xIndex) && isValidPriceY(yIndex))) continue; } ValidX[xIndex] = getX(xIndex); ValidY[yIndex] = getY(yIndex); ValidDataCounter++; } else { if(timeX == timeY) { if(!(isValidPriceX(xIndex) && isValidPriceY(yIndex)))//Если нужных котировок нет { Sleep(2000);//Ждем 2 секунды if(!(isValidPriceX(xIndex) && isValidPriceY(yIndex)))//И снова запрашиваем котировки continue;//Если их опять нет - преходим к следующему бару } ValidX[xIndex] = getX(xIndex); ValidY[yIndex] = getY(yIndex); ValidDataCounter++; } else if(timeX > timeY) { xIndex++;//Перескакиваем через проблемный бар в котировках первого инструмента } else if(timeX < timeY) { yIndex++;//Перескакиваем через бар в котировках второго инструмента } } } } //////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// //Возвращает коэффициент корреляции между двумя массивами данных //(в данном случае, радиус корреляции соответсвует размерам массивов, которые должны быть одинаковы) //////////////////////////////////////////////////////////////////////////////////////////// double getCorrelation(double X[], double Y[]) { if(ArraySize(X) != ArraySize(Y) || ArraySize(X) == 0)return(0.0); double avgX = 0, avgY = 0, covarXY = 0, dispX = 0, dispY = 0; int i, length = ArraySize(X); for(i = 0; i < length; i++) {avgX += X[i];avgY += Y[i];} avgX /= length;avgY /= length; for(i = 0; i < length; i++) { covarXY += (X[i]-avgX)*(Y[i]-avgY); dispX += (X[i] - avgX)*(X[i] - avgX); dispY += (Y[i] - avgY)*(Y[i] - avgY); } covarXY /= length; dispX /= length; dispY /= length; if(MathSqrt(dispX*dispY) > 0.0) return(covarXY/MathSqrt(dispX*dispY)); else return(0.0); } //////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// //Создание меток для вывода итоговых надписей внизу окна //////////////////////////////////////////////////////////////////////////////////////////// void CreateSummaryLabels() { IndicatorWindow = WindowFind(ShortName); if(IndicatorWindow == -1)return(-1); string Name = ShortName + SummaryLabel + "1"; if(ObjectFind(Name) == -1 && ObjectCreate(Name, OBJ_LABEL, IndicatorWindow, 0, 0)) { ObjectSet(Name, OBJPROP_CORNER, 2); ObjectSet(Name, OBJPROP_XDISTANCE, 0); ObjectSet(Name, OBJPROP_YDISTANCE, 0.7*FontSize); } Name = ShortName + SummaryLabel + "2"; if(ObjectFind(Name) == -1 && ObjectCreate(Name, OBJ_LABEL, IndicatorWindow, 0, 0)) { ObjectSet(Name, OBJPROP_CORNER, 2); ObjectSet(Name, OBJPROP_XDISTANCE, 0); ObjectSet(Name, OBJPROP_YDISTANCE, 2.5*FontSize); } } //////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// //Удаляются все объекты, в именах которых содержится строка //Phrase (расположенная в произвольной позиции) //////////////////////////////////////////////////////////////////////////////////////////// void DeleteObjectsByPhrase(string Phrase) { string ObjName; for(int i = ObjectsTotal()-1; i >= 0; i--) { ObjName = ObjectName(i); if(StringFind(ObjName, Phrase, 0) > -1) { ObjectDelete(ObjName); } } } //////////////////////////////////////////////////////////////////////////////////////////// double getSymbol(string Smbl, int shift, int Mode, int TimeFrame = 0) { double h = iHigh(Smbl, TimeFrame, shift), o = iOpen(Smbl, TimeFrame, shift), c = iClose(Smbl, TimeFrame, shift), l = iLow(Smbl, TimeFrame, shift); switch(Mode) { case CLOSE_MODE: return(c); case CLOSEOPEN_MODE: return(c - o); case CLOSE_RELATIVE_MODE: return(c/h); case CLOSEOPEN_RELATIVE_MODE: return((c - o)/h); case HIGHLOW_MODE: return(h - l); case HIGHLOW_RELATIVE_MODE: return((h - l)/h); case HIGHLOW_AVG_MODE: return((h + l)/2.0); case HIGHLOW_AVG_RELATIVE_MODE: return((h + l)/(2.0*h)); case FULL_AVG_MODE: return((h + l + c + o)/4.0); case FULL_AVG_RELATIVE_MODE: return((h + l + c + o)/(4.0*h)); } }